package com.ruoyi;// 导入所需的库
import javax.sound.sampled.*;
import java.awt.*;
import java.io.IOException;
import java.util.ArrayList;
import javax.swing.*;
import java.awt.event.*;
import org.jfree.chart.*;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.block.BlockBorder;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

public class SoundMeter {

    // 定义录音格式
    private static final int SAMPLE_RATE = 44100; // 采样率
    private static final double MAX_16_BIT = Short.MAX_VALUE; // 最大16位
    private static final int SAMPLE_SIZE_IN_BITS = 16; // 采样位数
    private static final int CHANNELS = 1; // 声道数
    private static final boolean SIGNED = true; // 是否有符号
    private static final boolean BIG_ENDIAN = true; // 大端表示
    private static final AudioFormat FORMAT = new AudioFormat(SAMPLE_RATE, SAMPLE_SIZE_IN_BITS, CHANNELS, SIGNED, BIG_ENDIAN); // 音频格式
    private static final int BUFFER_SIZE; // 缓冲区大小

    static {
        try {
            BUFFER_SIZE = AudioSystem.getTargetDataLine(FORMAT).getBufferSize();
        } catch (LineUnavailableException e) {
            throw new RuntimeException(e);
        }
    }

    private static final double REF_PRESSURE = 20; // 参考声压，单位微帕

    // 定义目标数据行，用于从麦克风读取音频数据
    private TargetDataLine line;

    // 定义一个标志，表示是否正在录音
    private boolean running;

    // 定义一个变量，用于存储实时分贝值的数据
    private XYSeries data;

    // 定义一个变量，用于记录数据的索引
    private int index;

    // 定义一个变量，用于显示折线图的面板
    private ChartPanel chartPanel;

    // 定义一个变量，用于创建窗口的框架
    private JFrame frame;

    // 定义一个变量，用于定时刷新折线图的计时器
    private Timer timer;

    // 构造方法，初始化目标数据行和数据变量
    public SoundMeter() throws LineUnavailableException {
        line = AudioSystem.getTargetDataLine(FORMAT);
        line.open(FORMAT, BUFFER_SIZE);
        data = new XYSeries("分贝值");
        index = 0;
    }

    // 开始录音
    public void start() {
        line.start();
        running = true;
        new Thread(this::run).start();
    }

    // 停止录音
    public void stop() {
        line.stop();
        line.close();
        running = false;
    }

    // 循环读取麦克风声音，并计算分贝值
    private void run() {
        byte[] buffer = new byte[BUFFER_SIZE];
        while (running) {
            // 读取一帧音频数据
            int bytesRead = line.read(buffer, 0, buffer.length);
            double sum = 0;
            // 将字节数据转换为 16 位有符号整数，计算样本的振幅
            for (int i = 0; i < bytesRead; i += 2) {
                short sample = (short) ((buffer[i + 1] << 8) | buffer[i]);
                double amplitude = sample / MAX_16_BIT;
                // 计算振幅的平方并累加
                sum += amplitude * amplitude;
            }
            // 平方和除以数据总长度，得到音量大小
            double rms = Math.sqrt(sum / (bytesRead / 2));
            // 计算分贝值
            double db = 20 * Math.log10(rms / REF_PRESSURE);
            // 输出分贝值 向上取整
            System.out.println("分贝值：" + (int) Math.ceil(db));
            long dbCeil = (long) Math.ceil(db);

            System.out.println("声音分贝值：" + dbCeil + " dB");
            // 将分贝值添加到数据变量中
            data.add(index, dbCeil);
            index++;
        }
    }

    // 创建折线图
    public void createChart() {
        // 创建一个折线图对象
        JFreeChart chart = ChartFactory.createXYLineChart(
                "实时分贝值", // 图表标题
                "时间", // x轴标签
                "分贝值", // y轴标签
                new XYSeriesCollection(data), // 数据集
                PlotOrientation.VERTICAL, // 图表方向
                true, // 是否显示图例
                true, // 是否显示工具提示
                false // 是否生成URL
        );
        // 设置折线图的样式
        XYPlot plot = chart.getXYPlot();
        XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();
        renderer.setSeriesPaint(0, Color.RED);
        renderer.setSeriesStroke(0, new BasicStroke(2.0f));
        plot.setRenderer(renderer);
        plot.setBackgroundPaint(Color.white);
        plot.setRangeGridlinesVisible(true);
        plot.setRangeGridlinePaint(Color.BLACK);
        plot.setDomainGridlinesVisible(true);
        plot.setDomainGridlinePaint(Color.BLACK);
        chart.getLegend().setFrame(BlockBorder.NONE);
        chart.setTitle(new TextTitle("实时分贝值",
                new Font("Serif", java.awt.Font.BOLD, 18)
        ));
        // 创建一个折线图面板
        chartPanel = new ChartPanel(chart);
    }

    // 创建窗口
    public void createFrame() {
        // 创建一个窗口框架
        frame = new JFrame("实时分贝值折线图");
        // 设置窗口的标题、大小、位置等
        frame.setTitle("实时分贝值折线图");
        frame.setSize(800, 600);
        frame.setLocationRelativeTo(null);
        // 将折线图面板添加到窗口中
        frame.add(chartPanel);
        // 让窗口可见
        frame.setVisible(true);
    }

    // 创建定时器
    public void createTimer() {
        // 创建一个定时器，每秒刷新一次
        timer = new Timer(1000, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // 获取折线图对象
                JFreeChart chart = chartPanel.getChart();
                // 获取 XYPlot 对象
                XYPlot plot = chart.getXYPlot();
                // 获取 XYDataset 对象
                XYSeriesCollection dataset = (XYSeriesCollection) plot.getDataset();
                // 更新数据
                dataset.addSeries( data);
            }
        });
        // 启动定时器
        timer.start();
    }

    // 测试方法
    public static void main(String[] args) throws LineUnavailableException, IOException {
        SoundMeter meter = new SoundMeter();
        meter.start();
        meter.createChart();
        meter.createFrame();
        meter.createTimer();
        System.in.read(); // 按任意键停止
        meter.stop();
    }
}
